package com.school.sports.service.Impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.school.sports.entity.FileInfo;
import com.school.sports.mapper.FileInfoMapper;
import com.school.sports.service.FileInfoService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

/**
 * 文件信息服务实现类
 */
@Service
public class FileInfoServiceImpl extends ServiceImpl<FileInfoMapper, FileInfo> implements FileInfoService {

    private static final Logger logger = LoggerFactory.getLogger(FileInfoServiceImpl.class);

    // 允许上传的图片文件类型
    private static final List<String> ALLOWED_IMAGE_TYPES = Arrays.asList(
            "jpg", "jpeg", "png", "gif", "bmp", "webp"
    );

    // 允许上传的文档文件类型
    private static final List<String> ALLOWED_DOCUMENT_TYPES = Arrays.asList(
            "pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt"
    );

    // 文件大小限制（50MB）
    private static final long MAX_FILE_SIZE = 50 * 1024 * 1024;

    @Value("${file.upload.path:/uploads}")
    private String uploadPath;

    @Value("${file.upload.domain:http://localhost:9090}")
    private String uploadDomain;

    @Override
    public IPage<FileInfo> getPage(int currentPage, int pageSize, FileInfo fileInfo) {
        Page<FileInfo> page = new Page<>(currentPage, pageSize);
        return baseMapper.selectFilePageWithUserName(page, fileInfo);
    }

    @Override
    public FileInfo uploadFile(MultipartFile file, String relatedType, Long relatedId, String description, Long uploadUserId) {
        if (file == null || file.isEmpty()) {
            throw new IllegalArgumentException("文件不能为空");
        }

        // 检查文件类型和大小
        if (!isAllowedFileType(file)) {
            throw new IllegalArgumentException("不支持的文件类型");
        }
        if (!isFileSizeAllowed(file)) {
            throw new IllegalArgumentException("文件大小超出限制");
        }

        try {
            // 计算文件MD5
            String fileMd5 = DigestUtils.md5DigestAsHex(file.getInputStream());

            // 检查文件是否已存在
            FileInfo existFile = checkFileExists(fileMd5);
            if (existFile != null) {
                logger.info("文件已存在，MD5: {}", fileMd5);
                return existFile;
            }

            // 生成文件路径
            String datePath = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
            String fileExtension = getFileExtension(file.getOriginalFilename());
            String newFileName = UUID.randomUUID().toString() + "." + fileExtension;
            String relativePath = datePath + "/" + newFileName;
            String fullPath = uploadPath + File.separator + relativePath;

            // 确保目录存在
            Path uploadDir = Paths.get(uploadPath, datePath);
            if (!Files.exists(uploadDir)) {
                Files.createDirectories(uploadDir);
            }

            // 保存文件
            file.transferTo(new File(fullPath));

            // 创建文件信息记录
            FileInfo fileInfo = new FileInfo();
            fileInfo.setFileName(newFileName);
            fileInfo.setOriginalName(file.getOriginalFilename());
            fileInfo.setFileExtension(fileExtension);
            fileInfo.setFilePath(relativePath);
            fileInfo.setFileUrl(uploadDomain + "/api/file/download/" + newFileName);
            fileInfo.setFileSize(file.getSize());
            fileInfo.setFileType(determineFileType(fileExtension));
            fileInfo.setMimeType(file.getContentType());
            fileInfo.setFileMd5(fileMd5);
            fileInfo.setUploadUserId(uploadUserId);
            fileInfo.setRelatedType(relatedType);
            fileInfo.setRelatedId(relatedId);
            fileInfo.setStatus("normal");
            fileInfo.setDescription(description);
            fileInfo.setCreatedAt(LocalDateTime.now());
            fileInfo.setUpdatedAt(LocalDateTime.now());

            // 保存到数据库
            save(fileInfo);

            logger.info("文件上传成功: {}", fileInfo.getOriginalName());
            return fileInfo;

        } catch (IOException e) {
            logger.error("文件上传失败: {}", e.getMessage(), e);
            throw new RuntimeException("文件上传失败: " + e.getMessage());
        }
    }

    @Override
    public List<FileInfo> uploadFiles(MultipartFile[] files, String relatedType, Long relatedId, String description, Long uploadUserId) {
        List<FileInfo> result = new ArrayList<>();
        for (MultipartFile file : files) {
            try {
                FileInfo fileInfo = uploadFile(file, relatedType, relatedId, description, uploadUserId);
                result.add(fileInfo);
            } catch (Exception e) {
                logger.error("批量上传文件失败: {}, 错误: {}", file.getOriginalFilename(), e.getMessage());
                // 继续处理其他文件
            }
        }
        return result;
    }

    @Override
    public void downloadFile(Long fileId, HttpServletResponse response) {
        FileInfo fileInfo = getById(fileId);
        if (fileInfo == null) {
            throw new IllegalArgumentException("文件不存在");
        }

        File file = new File(uploadPath + File.separator + fileInfo.getFilePath());
        if (!file.exists()) {
            throw new IllegalArgumentException("文件已被删除");
        }

        try {
            // 设置响应头
            response.setContentType(fileInfo.getMimeType());
            response.setHeader("Content-Disposition", "attachment; filename=\"" + fileInfo.getOriginalName() + "\"");
            response.setContentLengthLong(fileInfo.getFileSize());

            // 写入响应流
            try (FileInputStream fis = new FileInputStream(file);
                 BufferedInputStream bis = new BufferedInputStream(fis);
                 OutputStream os = response.getOutputStream()) {

                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = bis.read(buffer)) != -1) {
                    os.write(buffer, 0, bytesRead);
                }
                os.flush();
            }

            logger.info("文件下载成功: {}", fileInfo.getOriginalName());

        } catch (IOException e) {
            logger.error("文件下载失败: {}", e.getMessage(), e);
            throw new RuntimeException("文件下载失败: " + e.getMessage());
        }
    }

    @Override
    public boolean deletePhysicalFile(Long fileId) {
        FileInfo fileInfo = getById(fileId);
        if (fileInfo == null) {
            return false;
        }

        // 删除物理文件
        File file = new File(uploadPath + File.separator + fileInfo.getFilePath());
        boolean fileDeleted = file.exists() && file.delete();

        // 删除数据库记录
        boolean dbDeleted = removeById(fileId);

        logger.info("文件删除结果 - 物理文件: {}, 数据库记录: {}", fileDeleted, dbDeleted);
        return fileDeleted && dbDeleted;
    }

    @Override
    public String getPreviewUrl(Long fileId) {
        FileInfo fileInfo = getById(fileId);
        if (fileInfo == null) {
            return null;
        }

        // 图片文件可以直接预览
        if ("image".equals(fileInfo.getFileType())) {
            return uploadDomain + "/api/file/preview/" + fileInfo.getFileName();
        }

        // 其他类型返回下载URL
        return fileInfo.getFileUrl();
    }

    @Override
    public FileInfo checkFileExists(String fileMd5) {
        if (!StringUtils.hasText(fileMd5)) {
            return null;
        }
        return baseMapper.selectByMd5(fileMd5);
    }

    @Override
    public List<FileInfo> getFilesByRelatedInfo(String relatedType, Long relatedId) {
        if (!StringUtils.hasText(relatedType) || relatedId == null) {
            return new ArrayList<>();
        }
        return baseMapper.selectByRelatedInfo(relatedType, relatedId);
    }

    @Override
    public List<FileInfo> getFilesByUserId(Long uploadUserId) {
        if (uploadUserId == null) {
            return new ArrayList<>();
        }
        return baseMapper.selectByUploadUserId(uploadUserId);
    }

    @Override
    public Long getUserTotalFileSize(Long uploadUserId) {
        if (uploadUserId == null) {
            return 0L;
        }
        return baseMapper.selectTotalSizeByUserId(uploadUserId);
    }

    @Override
    public boolean isAllowedFileType(MultipartFile file) {
        if (file == null || file.getOriginalFilename() == null) {
            return false;
        }

        String fileExtension = getFileExtension(file.getOriginalFilename()).toLowerCase();
        return ALLOWED_IMAGE_TYPES.contains(fileExtension) || ALLOWED_DOCUMENT_TYPES.contains(fileExtension);
    }

    @Override
    public boolean isFileSizeAllowed(MultipartFile file) {
        return file != null && file.getSize() <= MAX_FILE_SIZE;
    }

    /**
     * 获取文件扩展名
     */
    private String getFileExtension(String filename) {
        if (!StringUtils.hasText(filename)) {
            return "";
        }
        int lastDotIndex = filename.lastIndexOf('.');
        return lastDotIndex > 0 ? filename.substring(lastDotIndex + 1) : "";
    }

    /**
     * 根据扩展名确定文件类型
     */
    private String determineFileType(String extension) {
        if (!StringUtils.hasText(extension)) {
            return "other";
        }

        String ext = extension.toLowerCase();
        if (ALLOWED_IMAGE_TYPES.contains(ext)) {
            return "image";
        } else if (ALLOWED_DOCUMENT_TYPES.contains(ext)) {
            return "document";
        } else {
            return "other";
        }
    }
}